#include "sync.h"
#include "backup.h"

void draw_progress_bar() {
    int bar_width = 40;
    float percentage = (float)written / dirSize; //当前进度的百分比
    int filled_width = bar_width * percentage; //需要填充的占比
    printf("[");
    int i;
    for (i = 0; i < filled_width; i++) {
        printf("#");
    }
    for (; i < bar_width; i++) {
        printf(" ");
    }
    printf("] %.2f%%\r", percentage * 100);
    fflush(stdout);
    if (percentage == 1) return;
    // pthread_exit(NULL);
    return;

}

void *copy_bigfile_thread_sync(void* arg) { //大文件的写入线程
    // printf("新线程\n");
    pthread_mutex_lock(&mutex); //上锁
    ThreadData* data = (ThreadData*)arg;
    // 重新定位文件头指针，为读写做准备
    fseek(data->source, data->start_offset, SEEK_SET);
    fseek(data->dest, data->start_offset, SEEK_SET);

    int buffer_size = data->end_offset - data->start_offset;
    char* buffer = malloc(buffer_size);

    //记录写的字节数，为画进度条作准备
    long bytes_read, bytes_written;
    long total_bytes_written = 0;

    while ((bytes_read = fread(buffer, 1, buffer_size, data->source)) > 0) {
        bytes_written = fwrite(buffer, 1, bytes_read, data->dest);
        total_bytes_written += bytes_written;
        written += bytes_written;
        draw_progress_bar();
        if (total_bytes_written >= buffer_size) { //段读写不会停止，需要手动终止
            break;
        }
    }
    // printf("线程结束\n");
    pthread_mutex_unlock(&mutex); // 解锁
    free(buffer);
    pthread_exit(NULL);
}

void copy_file_sync(const char* source_file, const char* dest_file) {

    FILE* source = fopen(source_file, "rb");
    FILE* dest = fopen(dest_file, "wb");
    
    // 定位文件，算出文件大小
    fseek(source, 0, SEEK_END);
    long file_size = ftell(source);
    fseek(source, 0, SEEK_SET);

    if (file_size <= FILE_MAX) {  // 小于等于500MB，使用单线程拷贝
        int buffer_size = BUFFER_SIZE;
        char* buffer = malloc(buffer_size);

        long bytes_read, bytes_written;
        
        // pthread_mutex_lock(&mutex);
        while ((bytes_read = fread(buffer, 1, buffer_size, source)) > 0) {
            bytes_written = fwrite(buffer, 1, bytes_read, dest);
            written += bytes_written;
            draw_progress_bar();
        }
        // pthread_mutex_unlock(&mutex);
        free(buffer);
    } else {  // 大于500MB，使用多线程拷贝
        // printf("\n文件过大，将开启多线程\n");
        pthread_t threads[THREAD_COUNT];
        ThreadData thread_data[THREAD_COUNT];

        // 每个线程的字节大小，大致的一个数据，因为会有余数，所以要对最后一个线程进行单独判断
        long chunk_size = file_size / THREAD_COUNT;

        for (int i = 0; i < THREAD_COUNT; i++) {
            thread_data[i].source = source;
            thread_data[i].dest = dest;
            thread_data[i].start_offset = i * chunk_size;
            // 对最后一个线程的末尾标记进行单独判断
            thread_data[i].end_offset = (i == THREAD_COUNT -1) ? file_size : (i + 1) * chunk_size;
            pthread_create(&threads[i], NULL, copy_bigfile_thread_sync, (void*)&thread_data[i]);

        }

        for (int i = 0; i < THREAD_COUNT; i++) {
            pthread_join(threads[i], NULL);
        }
    }

    //printf("\nFile copy complete.\n");
    //将源文件的权限赋给目标文件
    copyPermissions(source_file, dest_file);

    fclose(source);
    fclose(dest);
}

int has_dirent(const char* directory_path, const struct dirent* entry_to_find) {
    // printf("in has_dirent\n");
    DIR* dir = opendir(directory_path);
    if (dir == NULL) {
        perror("opendir");
        return 0; 
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, entry_to_find->d_name) == 0 &&
            entry->d_type == entry_to_find->d_type) {
            closedir(dir);
            return 1;  // 找到返回1
        }
    }

    closedir(dir);
    return 0;  // 未找到返回0
}

void delete_older_file(const char* file_path1, const char* file_path2) {
    struct stat file_info1, file_info2;

    if (stat(file_path1, &file_info1) == 0 && stat(file_path2, &file_info2) == 0) {
        time_t modify_time1 = file_info1.st_mtime;
        time_t modify_time2 = file_info2.st_mtime;

        if (modify_time1 < modify_time2) {
            if (unlink(file_path1) == 0) {
                // printf("\n已旧删除文件：%s\n", file_path1);
            } else {
                printf("无法旧删除文件：%s\n", file_path1);
            }
        } else {
            if (unlink(file_path2) == 0) {
                // printf("\n已删除旧文件：%s\n", file_path2);
            } else {
                printf("无法删除文件：%s\n", file_path2);
            }
        }
    } else {
        printf("无法获取文件信息\n");
    }
}

void delete_directory(const char* path) { //删除整个目录，包括其中的子目录和子文件
    DIR* dir = opendir(path);
    if (dir == NULL) {
        perror("opendir");
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char child_path[MAX_PATH_LENGTH];
        snprintf(child_path, sizeof(child_path) + 1, "%s/%s", path, entry->d_name);

        struct stat st;
        if (stat(child_path, &st) == 0) {
            if (S_ISDIR(st.st_mode)) {
                delete_directory(child_path);  // Recursive call for subdirectories
            } else {
                if (unlink(child_path) != 0) {
                    perror("unlink");
                }
            }
        } else {
            perror("stat");
        }
    }

    closedir(dir);

    if (rmdir(path) != 0) {
        perror("rmdir");
    }
}

void backup_diectory_sync(const char* source, const char* destination) {
    DIR* dir = opendir(source);
    if (dir == NULL) {
        printf("Failed to open source directory.\n");
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char source_path[MAX_PATH_LENGTH];
        char dest_path[MAX_PATH_LENGTH];
        snprintf(source_path, sizeof(source_path) + 1, "%s/%s", source, entry->d_name);
        snprintf(dest_path, sizeof(dest_path) + 1, "%s/%s", destination, entry->d_name);

        struct stat st;
        lstat(source_path, &st);

        if (S_ISDIR(st.st_mode)) {
            mkdir(dest_path, st.st_mode & 0777);
            backup_diectory_sync(source_path, dest_path);
        } else {
            copy_file_sync(source_path, dest_path);
        }
    }

    closedir(dir);
}

void sync_directory(char* source, char* destination, int mode) { //只负责清扫两个目录
    char* copy_source = source;
    char* copy_dest = destination;
    if (mode == 1) {
        // A为source， B为destination
        // B to A: 删除A中存在B中不存在的文件或目录，复制B中存在但A中不存在的部分，以B中的同名文件为准
        DIR* dir = opendir(source); //对A目录进行检查，删除多余的子项
        if (dir == NULL) {
            printf("Failed to open source directory.\n");
            pthread_exit(NULL);
        }

        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                continue;
            }

            // 源和目标目录下 子文件的路径，准备用于删除
            char source_path[MAX_PATH_LENGTH];
            char dest_path[MAX_PATH_LENGTH];
            snprintf(source_path, sizeof(source_path) + 1, "%s/%s", source, entry->d_name);
            snprintf(dest_path, sizeof(dest_path) + 1, "%s/%s", destination, entry->d_name);

            struct stat st;
            lstat(source_path, &st);

            // 1. 如果是目录，查找B中是否有该目录，若没有则删除A中该条目并continue。
            if (S_ISDIR(st.st_mode)) {
                if (!has_dirent(destination, entry)) { //若B中没有该目录
                    // 删除该目录
                    // printf("B 中没有该目录\n");
                    delete_directory(source_path);
                    continue;
                }
                sync_directory(source_path, dest_path, mode); // 若是目录文件，则还要递归查找子项
            }else { // 2.对文件进行判断
                if (!has_dirent(destination, entry)) {
                    if (remove(source_path) != 0) { // 删除A中有B中没有的文件
                        perror("remove");
                    }
                }
                else if (has_dirent(destination, entry)) { //查找B中与A的同名文件，将A中删除，以B的为标准
                    if (remove(source_path) != 0) {
                        perror("remove");
                    }
                }
            }
        }
        // copy_source = destination;
        // copy_dest = source;
        // 3. 将dest中文件复制给source ，直接在循环外调用backup接口
        //backup_diectory_sync(copy_source, copy_dest);
        closedir(dir);

    } else if (mode == 2) {
        // A为source，B为destination
        // A to B: 删除B中在A中不存在的文件或目录，复制A中存在但B中不存在的部分，以A中的同名文件为准
        DIR* dir = opendir(destination); //对B目录进行检查，删除多余的子项
        if (dir == NULL) {
            printf("Failed to open source directory.\n");
            pthread_exit(NULL);
        }

        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                continue;
            }

            // 源和目标目录下 子文件的路径，准备用于删除
            char source_path[MAX_PATH_LENGTH];
            char dest_path[MAX_PATH_LENGTH];
            snprintf(source_path, sizeof(source_path) + 1, "%s/%s", source, entry->d_name);
            snprintf(dest_path, sizeof(dest_path) + 1, "%s/%s", destination, entry->d_name);

            struct stat st;
            lstat(dest_path, &st);

            // 1. 如果是目录，查找A中是否有该目录，若没有则删除B中该条目并continue。
            if (S_ISDIR(st.st_mode)) {
                if (!has_dirent(source, entry)) { //若A中没有该目录
                    // 删除该目录
                    // printf("A 中没有该目录\n");
                    delete_directory(dest_path);
                    continue;
                }
                sync_directory(source_path, dest_path, mode); // 若是目录文件，则还要递归查找子项
            }else { // 2.对文件进行判断
                if (!has_dirent(source, entry)) {
                    if (remove(dest_path) != 0) { // 删除B中有A中没有的文件
                        perror("remove");
                    }
                }
                else if (has_dirent(source, entry)) { //查找A中与B的同名文件，将B中删除，以A的为标准
                    if (remove(dest_path) != 0) {
                        perror("remove");
                    }
                }
            }
        }
        // 3. 将source中文件复制给dest ，直接在循环外调用backup接口
        // dirSize = calculateDirectorySize(copy_source);
        // printf("\n****%lld\n", dirSize);
        // backup_diectory_sync(copy_source, copy_dest);
        closedir(dir);
    } else if (mode == 3) {
        // A为source，B为destination
        // AB双向同步: 默认以A文件为准检查B，找到同名文件比较最后更新时间，删除old
        DIR* dir = opendir(destination); //对B目录进行检查
        if (dir == NULL) {
            printf("Failed to open source directory.\n");
            pthread_exit(NULL);
        }

        struct dirent* entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                continue;
            }

            // 源和目标目录下 子项的路径，准备用于删除
            char source_path[MAX_PATH_LENGTH];
            char dest_path[MAX_PATH_LENGTH];
            snprintf(source_path, sizeof(source_path) + 1, "%s/%s", source, entry->d_name);
            snprintf(dest_path, sizeof(dest_path) + 1, "%s/%s", destination, entry->d_name);

            struct stat st;
            lstat(dest_path, &st);

            // 1. 如果是目录，且是A中有同名目录，就递归调用本函数，去检查目录下的文件
            if (S_ISDIR(st.st_mode)) {
                if (has_dirent(source, entry)) {
                    sync_directory(source_path, dest_path, mode); 
                }
            }else { // 2.如果是文件，且在A中有同名文件，则删除old
                if (has_dirent(source, entry)) { //查找A中与B的同名文件，将B中删除，以A的为标准
                    delete_older_file(source_path, dest_path);
                }
            }
        }
        // 3. 将文件两次重复复制，直接在循环外调用backup接口
        // dirSize = calculateDirectorySize(copy_source) + calculateDirectorySize(copy_dest);
        // printf("\n****%lld\n", dirSize);
        // backup_diectory_sync(copy_source, copy_dest);
        // backup_diectory_sync(copy_dest, copy_source);
        closedir(dir);
    }  
}

void* sync_directory_thread(void* arg) {
    SyncJob* job = (SyncJob*)arg;
    sync_directory(job->source, job->destination, job->mode); //只负责清扫两个目录
    if (job->mode == 1) { //根据对应mode进行拷贝 B2A
        dirSize = calculateDirectorySize(job->destination);
        printf("总共要【同步】的字节数：%lld\n", dirSize);
        backup_diectory_sync(job->destination, job->source);
    } else if (job->mode == 2) { //A2B
        dirSize = calculateDirectorySize(job->source);
        printf("总共要【同步】的字节数：%lld\n", dirSize);
        backup_diectory_sync(job->source, job->destination);
    } else if (job->mode == 3) { //AB
        long long olderDest = calculateDirectorySize(job->destination);
        dirSize = 2 * calculateDirectorySize(job->source) + calculateDirectorySize(job->destination);
        printf("总共要【同步】的字节数：%lld\n", dirSize);
        backup_diectory_sync(job->source, job->destination);
        backup_diectory_sync(job->destination, job->source);
    }
    pthread_exit(NULL);
}
